/**
 * 
 */
package com.google.code.sip.ucenter.service.impl;

import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.mvel2.MVEL;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.google.code.sip.core.dao.Page;
import com.google.code.sip.core.utils.DateUtils;
import com.google.code.sip.ucenter.dao.SettingDao;
import com.google.code.sip.ucenter.dao.mongo.CreditDaoImpl;
import com.google.code.sip.ucenter.dao.mongo.CreditLogDaoImpl;
import com.google.code.sip.ucenter.dao.mongo.CreditRuleDaoImpl;
import com.google.code.sip.ucenter.dao.mongo.CreditRuleLogDaoImpl;
import com.google.code.sip.ucenter.dao.mongo.CreditTypeDaoImpl;
import com.google.code.sip.ucenter.dao.mongo.UserCountDaoImpl;
import com.google.code.sip.ucenter.dao.mongo.UserDaoImpl;
import com.google.code.sip.ucenter.model.Credit;
import com.google.code.sip.ucenter.model.CreditLog;
import com.google.code.sip.ucenter.model.CreditOperation;
import com.google.code.sip.ucenter.model.CreditRule;
import com.google.code.sip.ucenter.model.CreditRuleAmount;
import com.google.code.sip.ucenter.model.CreditRuleCycleType;
import com.google.code.sip.ucenter.model.CreditRuleLog;
import com.google.code.sip.ucenter.model.CreditType;
import com.google.code.sip.ucenter.model.Setting;
import com.google.code.sip.ucenter.model.User;
import com.google.code.sip.ucenter.model.UserCount;
import com.google.code.sip.ucenter.service.CreditManager;

/**
 * @author Liuye
 * 
 */
@Service("creditManager")
public class CreditManagerImpl implements CreditManager {
	@Autowired
	private CreditDaoImpl creditDao;
	@Autowired
	private CreditTypeDaoImpl creditTypeDao;
	@Autowired
	private CreditLogDaoImpl creditLogDao;
	@Autowired
	private CreditRuleDaoImpl creditRuleDao;
	@Autowired
	private CreditRuleLogDaoImpl creditRuleLogDao;
	@Autowired
	private SettingDao settingDao;
	@Autowired
	private UserCountDaoImpl userCountDao;
	@Autowired
	private UserDaoImpl userDao;

	@Override
	public List<Credit> getCreditesByUser(User user) {
		// User user = userDao.get(userId);
		return getByUser(user);
	}

	public List<Credit> getByUser(User user) {
		if (user != null) {
			return creditDao.findByUser(user);// .getId());
		}
		return null;
	}

	@Override
	public Credit getCredit(User user, CreditType creditType) {
		Map<String, Object> filters = new LinkedHashMap<String, Object>();
		filters.put("user", user);
		filters.put("creditType", creditType);
		Credit credit = creditDao.getByProperties(filters);
		return credit;
	}

	@Override
	public void grantCredit(User user, String action, String remark) {
		// User user = userDao.get(userId);
		if (user != null) {
			CreditRule creditRule = creditRuleDao.getByAction(action);
			if (creditRule != null) {
				Set<CreditRuleAmount> creditRuleAmounts = creditRule
						.getCreditRuleAmounts();
				if (creditRuleAmounts != null && creditRuleAmounts.size() > 0) {
					for (CreditRuleAmount creditRuleAmount : creditRuleAmounts) {
						CreditType creditType = creditRuleAmount
								.getCreditType();
						// if (creditRuleAmount.getAmout() > 0) { this line
						// modified by fengcai 2010/10/18
						if (creditRuleAmount.getAmout() != 0) {
							// 增加积分规则日志
							CreditRuleLog creditRuleLog = creditRuleLogDao
									.getByUserAndRule(user, creditRule);
							if (creditRuleLog == null) {
								creditRuleLog = new CreditRuleLog();
								creditRuleLog.setUser(user);
								creditRuleLog.setCreditRule(creditRule);
							}
							// 重置当天的积分计算次数
							Date today = new Date();
							if (!DateUtils.isSameDay(today, creditRuleLog
									.getDateLine())) {
								// creditRuleLog.setDateLine(new Date());
								creditRuleLog.setCycleNum(0);
							}
							// 保存积分
							if (canGrantCredit(creditRule, creditRuleLog)) {
								creditRuleLog.setDateLine(new Date()); // add
								// by
								// fengcai
								// 2010/7/7
								// 时间应为最后发表的时间
								creditRuleLog.setCycleNum(creditRuleLog
										.getCycleNum() + 1);
								creditRuleLog
										.setTotal(creditRuleLog.getTotal() + 1);
								creditRuleLogDao.save(creditRuleLog);
								// 保存用户积分
								if (creditType != null
										&& creditType.isAvailable()) {
									Credit credit = getOrCreateCredit(user,
											creditType);
									credit.setAmount(credit.getAmount()
											+ creditRuleAmount.getAmout());
									saveCreditLog(user, creditType, creditRule,
											creditRuleAmount.getAmout(),
											CreditOperation.TRC, remark, false);
									this.saveCredit(credit);
								}
							}
						}
					}
				}
			}
			// 更新用户的总积分
			updateUserCreditCount(user);
		}
	}

	/**
	 * 判断是否可以计算积分
	 * 
	 * @param creditRule
	 * @param creditRuleLog
	 * @return
	 */
	public boolean canGrantCredit(CreditRule creditRule,
			CreditRuleLog creditRuleLog) {
		if (creditRule.getCycleType() == null) {
			return false;
		}
		if (CreditRuleCycleType.NotLimit.equals(creditRule.getCycleType())) {
			return true;
		}
		Date today = new Date();
		if (CreditRuleCycleType.One.equals(creditRule.getCycleType())) {
			if (!DateUtils.isSameDay(today, creditRuleLog.getDateLine())) {
				return true;
			} else if (creditRuleLog.getCycleNum() < 1) {
				return true;
			} else {
				return false;
			}
		}
		if (CreditRuleCycleType.EveryDay.equals(creditRule.getCycleType())) {
			if (!DateUtils.isSameDay(today, creditRuleLog.getDateLine())) {
				return true;
			} else if (creditRuleLog.getCycleNum() < creditRule.getRewardNum()
					|| creditRule.getRewardNum() == 0) {
				return true;
			} else {
				return false;
			}
		}
		return true;
	}

	@Override
	public List<CreditLog> getCreditLogs(User user) {
		return getUserCreditLogs(user);
	}

	@Override
	public Page<CreditLog> getCreditLogs(User user, Page<CreditLog> page) {
		return getUserCreditLogs(user, page);
	}

	public List<CreditLog> getUserCreditLogs(User user) {
		if (user != null) {
			return creditLogDao.findByUser(user);
		}
		return null;
	}

	public Page<CreditLog> getUserCreditLogs(User user, Page<CreditLog> page) {
		if (user != null) {
			return creditLogDao.findByUser(user, page);
		}
		return null;
	}

	@Override
	public void deleteCredit(String creditId) {
		creditDao.delete(creditId);
	}

	@Override
	public Credit getCredit(String d) {
		return creditDao.get(d);
	}

	@Override
	public Page<Credit> getAllCredites(Page<Credit> page) {
		return creditDao.findByPage(page);
	}

	@Override
	public List<Credit> getAllCredites() {
		return creditDao.findAll();
	}

	@Override
	public void saveCredit(Credit entity) {
		creditDao.save(entity);
	}

	@Override
	public List<CreditRule> getAllCreditRules() {
		return creditRuleDao.findAll();
	}

	@Override
	public List<CreditType> getAllCreditTypes() {
		return creditTypeDao.findAll();
	}

	@Override
	public void transferCredit(Credit outCredit, Credit intCredit, int amount,
			String remark) {
		// 根据积分交易税计算转入积分数
		Setting creditTansferTaxSetting = settingDao
				.getByName(Setting.CREDIT_TAX);
		double creditTansferTax = creditTansferTaxSetting != null ? Double
				.parseDouble(creditTansferTaxSetting.getValue()) : 0d;
		int inCreditAmount = 0;
		if (creditTansferTax != 0) {
			inCreditAmount = (int) (amount * (1 - creditTansferTax));
		} else {
			inCreditAmount = amount;
		}
		// 积分转让
		outCredit.setAmount(outCredit.getAmount() - amount);
		intCredit.setAmount(intCredit.getAmount() + inCreditAmount);
		creditDao.save(outCredit);
		creditDao.save(intCredit);
		// 产生积分日志
		saveCreditLog(outCredit.getUser(), outCredit.getCreditType(), null,
				amount, CreditOperation.TFR, remark, false);
		saveCreditLog(intCredit.getUser(), intCredit.getCreditType(), null,
				inCreditAmount, CreditOperation.RCV, remark, false);
	}

	@Override
	public String buy(Credit credit, CreditOperation creditOperation,
			int amount, String remark) {
		credit.setAmount(credit.getAmount() - amount);
		saveCredit(credit);
		// 产生积分日志
		String creditLogId = saveCreditLog(credit.getUser(), credit
				.getCreditType(), null, amount, creditOperation, remark);
		// 更新用户的总积分
		updateUserCreditCount(credit.getUser());
		return creditLogId;
	}

	@Override
	public void cancelBuy(User user, CreditType creditType,
			String transactionId, boolean deductedTax,
			CreditOperation creditOperation) {
		Credit credit = getOrCreateCredit(user, creditType);
		CreditLog creditLog = creditLogDao.get(transactionId);
		int inCreditAmount = 0;
		if (deductedTax) {
			// 根据积分交易税计算转入积分数
			Setting creditTansferTaxSetting = settingDao
					.getByName(Setting.CREDIT_TAX);
			double creditTansferTax = creditTansferTaxSetting != null ? Double
					.parseDouble(creditTansferTaxSetting.getValue()) : 0d;
			if (creditTansferTax != 0) {
				inCreditAmount = (int) (creditLog.getAmount() * (1 - creditTansferTax));
			} else {
				inCreditAmount = creditLog.getAmount();
			}
		} else {
			inCreditAmount = creditLog.getAmount();
		}
		credit.setAmount(credit.getAmount() + inCreditAmount);
		saveCredit(credit);
		// 产生积分日志
		saveCreditLog(credit.getUser(), credit.getCreditType(), null,
				inCreditAmount, creditOperation, null, true);
		// 更新用户的总积分
		updateUserCreditCount(user);
	}

	@Override
	public void gainCredit(User user, CreditType creditType,
			CreditOperation creditOperation, boolean deductedTax, int amount,
			String remark) {

		Credit credit = getOrCreateCredit(user, creditType);
		if (deductedTax) {
			// 根据积分交易税计算转入积分数
			Setting creditTansferTaxSetting = settingDao
					.getByName(Setting.CREDIT_TAX);
			double creditTansferTax = creditTansferTaxSetting != null ? Double
					.parseDouble(creditTansferTaxSetting.getValue()) : 0d;
			if (creditTansferTax != 0) {
				amount = (int) (amount * (1 - creditTansferTax));
			}
		}
		credit.setAmount(credit.getAmount() + amount);
		saveCredit(credit);
		// 产生积分日志
		saveCreditLog(credit.getUser(), credit.getCreditType(), null, amount,
				creditOperation, remark, false);
		// 更新用户的总积分
		updateUserCreditCount(user);
	}

	@Override
	public void sell(User user, CreditType creditType, int amount, String remark) {
		Setting creditTansferTaxSetting = settingDao
				.getByName(Setting.CREDIT_TAX);
		double creditTansferTax = creditTansferTaxSetting != null ? Double
				.parseDouble(creditTansferTaxSetting.getValue()) : 0d;
		if (creditTansferTax != 0) {
			amount = (int) (amount * (1 - creditTansferTax));
		}
		Credit credit = getOrCreateCredit(user, creditType);
		credit.setAmount(credit.getAmount() + amount);
		saveCredit(credit);
		// 产生积分日志
		saveCreditLog(credit.getUser(), credit.getCreditType(), null, amount,
				CreditOperation.SMC, remark, false);
		// 更新用户的总积分
		updateUserCreditCount(user);
	}

	@Override
	public void exchange(User user, CreditType creditTypeFrom,
			CreditType creditTypeTo, int amount, String remark) {
		Credit creditFrom = getOrCreateCredit(user, creditTypeFrom);
		Credit creditTo = getOrCreateCredit(user, creditTypeTo);
		// 积分交易税
		Setting creditTansferTaxSetting = settingDao
				.getByName(Setting.CREDIT_TAX);
		double creditTansferTax = creditTansferTaxSetting != null ? Double
				.parseDouble(creditTansferTaxSetting.getValue()) : 0d;
		float fromRadio = creditTypeFrom.getRatio();
		float toRadio = creditTypeTo.getRatio();
		double radio = toRadio / fromRadio;// creditTypeFrom.getRatio()/creditTypeTo.getRatio();
		int exchangeAmount = 0;
		if (radio > 1) {
			exchangeAmount = (int) Math.ceil(amount
					/ (radio * (1 + creditTansferTax)));
		}
		if (radio < 1) {
			exchangeAmount = (int) Math.floor(amount
					/ (radio * (1 + creditTansferTax)));
		}
		if (radio == 1) {
			exchangeAmount = (int) Math.floor(amount
					/ (radio * (1 + creditTansferTax)));
		}
		// int exchangeAmount = (int) (amount*radio);

		// 更新出帐Credit
		creditFrom.setAmount(creditFrom.getAmount() - amount);
		saveCredit(creditFrom);
		// 产生积分日志
		saveCreditLog(user, creditTypeFrom, null, amount, CreditOperation.EXO,
				remark, false);

		// 更新入账Credit
		creditTo.setAmount(creditTo.getAmount() + exchangeAmount);
		saveCredit(creditTo);
		// 产生积分日志
		saveCreditLog(user, creditTypeTo, null, amount, CreditOperation.EXI,
				remark, false);
		// 更新用户的总积分
		updateUserCreditCount(user);
	}

	protected String saveCreditLog(User user, CreditType creditType,
			CreditRule creditRule, int amount, CreditOperation operation,
			String remark) {
		CreditLog creditLog = new CreditLog();
		creditLog.setUsername(user.getUsername());
		creditLog.setNickName(user.getNickName());
		creditLog.setCreatedDate(new Date());
		creditLog.setCreditTypeName(creditType.getName());
		creditLog.setCreditTypeTitle(creditType.getTitle());
		if (creditRule != null) {
			creditLog.setCreditRuleName(creditRule.getName());
			creditLog.setCreditRuleAction(creditRule.getAction());
		}
		creditLog.setOperation(operation);
		creditLog.setAmount(amount);
		if (StringUtils.isNotBlank(remark)) {
			creditLog.setRemark(remark);
		}
		creditLogDao.save(creditLog);
		return creditLog.getId();
	}

	protected void saveCreditLog(User user, CreditType creditType,
			CreditRule creditRule, int amount, CreditOperation operation,
			String remark, Boolean canceled) {
		CreditLog creditLog = new CreditLog();
		creditLog.setUsername(user.getUsername());
		creditLog.setNickName(user.getNickName());
		creditLog.setCreatedDate(new Date());
		creditLog.setCreditTypeName(creditType.getName());
		creditLog.setCreditTypeTitle(creditType.getTitle());
		if (creditRule != null) {
			creditLog.setCreditRuleName(creditRule.getName());
			creditLog.setCreditRuleAction(creditRule.getAction());
		}
		creditLog.setOperation(operation);
		creditLog.setAmount(amount);
		if (StringUtils.isNotBlank(remark)) {
			creditLog.setRemark(remark);
		}
		creditLog.setCanceled(canceled);
		creditLogDao.save(creditLog);
	}

	@Override
	public List<CreditRuleLog> getAllCreditRuleLog(User user) {
		return creditRuleLogDao.findByUser(user);
	}

	@Override
	public CreditRuleLog getCreditRuleLog(User user, CreditRule creditRule) {
		return creditRuleLogDao.getByUserAndRule(user, creditRule);
	}

	/**
	 * 更新用户总积分
	 * 
	 * @param user
	 */
	@Override
	public void updateUserCreditCount(User user) {
		UserCount userCount = getOrCreateUserCreditCount(user);
		userCount.setCreditCount(calcUserCreditCount(user));
		userCountDao.save(userCount);
	}

	@Override
	public UserCount getOrCreateUserCreditCount(User user) {
		UserCount userCount = userCountDao.getByUser(user);
		if (userCount == null) {
			userCount = new UserCount();
			userCount.setUser(user);
		}
		return userCount;
	}

	/**
	 * 计算用户总积分
	 * 
	 * @param user
	 * @return
	 */
	@Override
	public int calcUserCreditCount(User user) {
		// 获取总积分计算公式
		Setting creditFormulaSetting = settingDao
				.getByName(Setting.CREDIT_FORMULA);
		String formula = creditFormulaSetting.getValue();
		// 获取用户积分
		List<Credit> credits = creditDao.findByUser(user);
		// 根据积分公式计算总积分
		if (StringUtils.isNotBlank(formula)) {
			List<CreditType> creditTypes = creditTypeDao.findAll();
			Map<String, Object> vars = new HashMap<String, Object>(creditTypes
					.size());
			for (CreditType creditType : creditTypes) {
				vars.put(creditType.getName(), getUserCreditAmount(creditType,
						credits));
			}
			return MVEL.eval(formula, vars, Integer.class);
		} else {// 无积分公式，总积分等于各积分之和
			int total = 0;
			for (Credit credit : credits) {
				total += credit.getAmount();
			}
			return total;
		}

		// 根据系统设置的积分公式获取真正的可计算公式
		// String formula = creditFormulaSetting.getValue();
		// if (StringRegexUtils.isNotBlank(formula.trim())) {
		// formula = formula.replaceAll(" ", "");
		// String[] fomulaElements = formula.split("\\+|-");
		// for (String fomulaElement : fomulaElements) {
		// String[] elements = fomulaElement.split("\\*|/");
		// for (String element : elements) {
		// boolean startWidth = element.startsWith("(");
		// boolean endWidth = element.endsWith(")");
		// String creditTypeName = element;
		// if (startWidth) {
		// creditTypeName = element.substring(1);
		// }
		// if (endWidth) {
		// creditTypeName = creditTypeName.substring(0,
		// creditTypeName.length() - 1);
		// }
		// if (!NumberUtils.isNumber(creditTypeName)) {
		// Credit credit = getCreditInCreditListByTypeName(
		// credits, creditTypeName);
		// if (credit == null) {
		// throw new ServiceException("非法用户总积分计算公司。");
		// }
		// StringBuilder replace = new StringBuilder();
		// if (startWidth) {
		// replace.append("(");
		// }
		// replace.append(credit.getAmount());
		// if (endWidth) {
		// replace.append(")");
		// }
		// formula = formula.replace(element, replace);
		// }
		// }
		// }
		// }
		//
		// ExpressionParser parser = new SpelExpressionParser();
		// int creditCount = parser.parseExpression(formula).getValue(
		// Integer.class);
		// return creditCount;

	}

	// private Credit getCreditInCreditListByTypeName(List<Credit> credits,
	// String creditTypeName) {
	// for (int i = 0; i < credits.size(); i++) {
	// if (credits.get(i).getCreditType().getName().equals(creditTypeName)) {
	// return credits.get(i);
	// }
	// }
	// return null;
	// }

	private int getUserCreditAmount(CreditType creditType, List<Credit> credits) {
		for (Credit credit : credits) {
			if (credit.getCreditType().equals(creditType)) {
				return credit.getAmount();
			}
		}
		return 0;
	}

	@Override
	public CreditRule getCreditRuleByAction(String action) {
		return creditRuleDao.getByAction(action);
	}

	@Override
	public CreditType getCreditTypeByName(String name) {
		return creditTypeDao.getByName(name);
	}

	@Override
	public void chargeCredit(User user, double money, CreditType creditType,
			int creditChargeRatio, String remark) {
		Credit credit = getOrCreateCredit(user, creditType);
		// 获取现金/积分兑换比率
		int inAmount = (int) (money * creditChargeRatio);
		credit.setAmount(credit.getAmount() + inAmount);
		creditDao.save(credit);
		// 记录日志
		saveCreditLog(user, creditType, null, inAmount, CreditOperation.AFD,
				remark, false);
		// 更新用户的总积分
		updateUserCreditCount(user);
	}

	@Override
	public List<Credit> getCreditesByUsers(List<User> users) {
		return creditDao.findWithIn("user", users);
	}

	@Override
	public Credit getOrCreateCredit(User user, CreditType creditType) {
		Credit credit = this.getCredit(user, creditType);
		if (credit == null) {
			credit = new Credit();
			credit.setCreditType(creditType);
			credit.setUser(user);
			credit.setAmount(0);
			this.saveCredit(credit);
		}
		return credit;
	}

	@Override
	public void reCalcUserCreditCount() {
		Iterator<User> iter = userDao.fetch().iterator();
		while (iter.hasNext()) {
			updateUserCreditCount(iter.next());
		}
	}
}
